﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Markup;
using RandomSearch.PluginsCommon;

namespace RandomSearch.Plugins.DefaultSearch {
    [Export(typeof(ISearchPlugin))]
    public class TextSearch : BaseSearchPlugin {
        private const string TextToSerachSettingName = "TextSearch_Text";

        private const long SmallFileMaxSize = 1024;

        [ImportingConstructor]
        public TextSearch() { }

        public override string PatternToSearch {
            get { return "*.txt"; }
        }

        public override string PluginId {
            get { return "Text"; }
        }

        public override string PluginName {
            get { return "Text search"; }
        }

        public override string Options {
            get {
                var pnl = new StackPanel();
                var lb = new Label { Content = "Text to search" };
                var tbText = new TextBox { Name = TextToSerachSettingName };
                pnl.Children.Add(lb);
                pnl.Children.Add(tbText);
                var settings = XamlWriter.Save(pnl);
                return settings;
            }
        }

        private static bool MathcCriteria(SearchResult result, TextSearchOptions options) {
            var file = new FileInfo(result.File);

            //can be too larg to load into memory
            if (file.Length > SmallFileMaxSize)
                return LargeFileMatch(result, options);

            string content = file.OpenText().ReadToEnd();
            return (0 >= content.IndexOf(options.PatternToSearch, StringComparison.OrdinalIgnoreCase));
        }

        private static bool LargeFileMatch(SearchResult result, TextSearchOptions options) {
            var file = new FileInfo(result.File);

            string textToSearch = options.PatternToSearch;

            var windowSize = 100 * textToSearch.Length;
            var buf = new char[windowSize];

            var sb = new StringBuilder();
            var tmp = new char[textToSearch.Length];

            int currentPos = 0;
            using (var fStream = file.OpenText()) {
                while (!fStream.EndOfStream) {
                    int n = fStream.ReadBlock(buf, 0, buf.Length);
                    sb.Clear();
                    if (currentPos > 0) {
                        sb.Append(tmp);
                    }
                    sb.Append(buf);
                    var content = sb.ToString();
                    var res = (0 < content.IndexOf(options.PatternToSearch, StringComparison.OrdinalIgnoreCase));

                    if (res) return true;

                    if (n >= tmp.Length) Array.Copy(buf, buf.Length - tmp.Length - 1, tmp, 0, tmp.Length);
                    currentPos += buf.Length;
                }
            }
            return false;
        }

        public override IEnumerable<SearchResult> Search(SearchOptions options) {
            IEnumerable<SearchResult> baseSearchResults = base.Search(options);
            var tsOptions = new TextSearchOptions(options);
            return baseSearchResults.Where(x => MathcCriteria(x, tsOptions));
        }

        #region Nested type: TextSearchOptions

        private class TextSearchOptions {
            public TextSearchOptions(SearchOptions options) {
                PatternToSearch = options.Settings[TextToSerachSettingName];
            }

            public string PatternToSearch { get; private set; }
        }

        #endregion
    }
}